/*==============================================================================

  Copyright (c) Laboratory for Percutaneous Surgery (PerkLab)
  Queen's University, Kingston, ON, Canada. All Rights Reserved.

  See COPYRIGHT.txt
  or http://www.slicer.org/copyright/copyright.txt for details.

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

  This file was originally developed by Adam Rankin and Csaba Pinter, PerkLab, Queen's
  University and was supported through the Applied Cancer Research Unit program of Cancer
  Care Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care

==============================================================================*/

#ifndef __vtkMRMLSegmentationStorageNode_h
#define __vtkMRMLSegmentationStorageNode_h

// Temporarily keep support for reading nrrd files that are saved as 4D spatial image.
// The current way of writing volume is 3 spatial dimension and a list.
#define SUPPORT_4D_SPATIAL_NRRD

// MRML includes
#include "vtkMRMLStorageNode.h"

#ifdef SUPPORT_4D_SPATIAL_NRRD
  // ITK includes
  #include <itkImageRegionIteratorWithIndex.h>
#endif

class vtkMRMLSegmentationNode;
class vtkMatrix4x4;
class vtkPolyData;
class vtkOrientedImageData;
class vtkSegmentation;
class vtkMRMLSegmentationNode;
class vtkSegment;
class vtkInformationStringKey;
class vtkInformationIntegerVectorKey;

/// \brief MRML node for segmentation storage on disk.
///
/// Storage nodes has methods to read/write segmentations to/from disk.
///
/// Only the master representation of the segmentation is stored on disk.
/// If master representation is labelmap then it is stored as a 4D image volume
/// (.seg.nrrd file). If master representation is polygonal mesh, such as closed
/// surface, then the segmentation is stored as a VTK multiblock data set
/// (.vtm file accompanied by a number of .vtk polydata files).
///
/// Specification of .seg.nrrd file:
///
/// Segmentation is stored in a standard 4D NRRD image file
/// (one "list" dimension and 3 spatial dimensions). Some additional metadata
/// is stored in custom data fields (starting with Segmentation_ and SegmentN_
/// prefix), which provide hints on how the segments should be displayed
/// or what they contain. Each 3D volume is a "layer" in the image can contain many
/// non-overlapping segments.
/// If no segments within the segmentation overlap, then the segmentation will be
/// saved as a 3D volume. Upon saving, segments are automatically collapsed to as
/// few layers as possible.
///
/// Common custom fields:
///
/// - Segmentation_ContainedRepresentationNames: names of segmentation representations
///   (separated by | character) that should be displayed. Common representation
///   names include "Binary labelmap", "Closed surface", "Planar contours", "Fractional labelmap"
/// - Segmentation_ConversionParameters: parameters for algorithms that compute requested
///   representations. Each parameter definition is separated by & character. A parameter
///   definition consists of parameter name, value, and description text, separated by
///   | character.
/// - Segmentation_MasterRepresentation: defines what representation is stored in the file.
///   It is most commonly "Binary labelmap", but there are other representations, too, which
///   are stored in an image volume - such as "Fractional labelmap".
/// - Segmentation_ReferenceImageExtentOffset: This field allows storing an image with arbitrary
///   extent in a NRRD file. NRRD file only stores size of the volume. This field stores
///   voxel coordinates (separated by spaces), therefore the extent can be computed as
///   (ReferenceImageExtentOffsetI, ReferenceImageExtentOffsetI+sizeI-1,
///   ReferenceImageExtentOffsetJ, ReferenceImageExtentOffsetJ+sizeJ-1,
///   ReferenceImageExtentOffsetK, ReferenceImageExtentOffsetk+sizeK-1).
///
/// Custom fields for N-th segment (N is between 0 and size of the first axis - 1):
///
/// - SegmentN_ID: identifier of the segment, it is unique within the segmentation. Can be used for unanmiguously refer to a segment
///   even if the segment's display name is changed.
/// - SegmentN_Name: displayable name of the segment.
/// - SegmentN_NameAutoGenerated: if value is 0 then it means that SegmentN_Name was chosen manually by the user, if value is 1 then it means
///   that the segment's name is generated automatically (for example, determined from terminology).
/// - SegmentN_Color: recommended segment display color, defined with red, green, blue values (between 0.0-1.0) separated by space character
/// - SegmentN_ColorAutoGenerated: if value is 0 then it means that SegmentN_Color was chosen manually by the user, if value is 1 then it means
///   that the segment's color is generated automatically (for example, color defined based on terminology).
/// - SegmentN_Extent: 6 space-separated values (minI, maxI, minJ, maxJ, minZ, maxZ) defining extent of non-empty region within the segment.
/// - SegmentN_Tags: List of key:value pairs for storing additional information about the segmen. Key:value pair is separated by a : character,
///   pairs are separated from each other by | character.
/// - SegmentN_Layer: Index of the 3D volume that contains the segment. For segmentations in which the segments do not overlap, the segmentation can
///   be represented as a single 3D volume with 1 layer. Upon saving, segments are automatically collapsed to as few layers as possible.
/// - SegmentN_LabelValue: The scalar value used to represent the segment within its own layer. Segments on separate layers can have the same label value.
///
/// A frequently used key is "TerminologyEntry", which defines what the segment contains using DICOM compliant terminology. Value stores
/// 7 parts: terminology context name, category, type, type modifier, anatomic context name, anatomic region, and anatomic region modifier.
/// Parts are separated from each other by ~ character. Five of these parts - category, type, type modifier, anatomic region, and
/// anatomic region modifier are defined by a triplet of (coding scheme designator, code value, and code meaning). Components of the triplet
/// are separated by ^ character.
///
///   Example: a mass in the right side of the adrenal gland would be encoded with the tag:
///
///   "TerminologyEntry:
///    Segmentation category and type - 3D Slicer General Anatomy list
///    ~SCT^49755003^Morphologically Altered Structure
///    ~SCT^4147007^Mass
///    ~^^
///    ~Anatomic codes - DICOM master list
///    ~SCT^23451007^Adrenal gland
///    ~SCT^24028007^Right"
///
///   Interpretation:
///
///   - terminology context name: Segmentation category and type - 3D Slicer General Anatomy list
///   - category: codingScheme=SCT, codeValue=49755003, codeMeaning=Morphologically Altered Structure
///   - type: codingScheme=SCT, codeValue=4147007, codeMeaning=Mass
///   - type modifier: not specified
///   - anatomic context name: Anatomic codes - DICOM master list
///   - anatomic region: codingScheme=SCT, codeValue=23451007, codeMeaning=Adrenal gland
///   - anatomic region modifier: codingScheme=SCT, codeValue=^24028007, codeMeaning=Right
///

class VTK_MRML_EXPORT vtkMRMLSegmentationStorageNode : public vtkMRMLStorageNode
{
#ifdef SUPPORT_4D_SPATIAL_NRRD
  // Although internally binary labelmap representations can be of unsigned char, unsigned short
  // or short types, the output file is always unsigned char
  //TODO: This is a limitation for now
  typedef itk::Image<unsigned char, 4> BinaryLabelmap4DImageType;
  typedef itk::ImageRegionIteratorWithIndex<BinaryLabelmap4DImageType> BinaryLabelmap4DIteratorType;
#endif

public:
  static vtkMRMLSegmentationStorageNode *New();
  vtkTypeMacro(vtkMRMLSegmentationStorageNode, vtkMRMLStorageNode);
  void PrintSelf(ostream& os, vtkIndent indent) override;

  vtkMRMLNode* CreateNodeInstance() override;

  /// Read node attributes from XML file
  void ReadXMLAttributes( const char** atts) override;

  /// Write this node's information to a MRML file in XML format.
  void WriteXML(ostream& of, int indent) override;

  /// Copy the node's attributes to this object
  void Copy(vtkMRMLNode *node) override;

  /// Get node XML tag name (like Storage, Model)
  const char* GetNodeTagName() override {return "SegmentationStorage";}

  /// Return a default file extension for writing
  /// File write extension is determined dynamically
  /// from master representation type.
  const char* GetDefaultWriteFileExtension() override;

  /// Return true if the reference node can be read in
  bool CanReadInReferenceNode(vtkMRMLNode *refNode) override;

  /// Reset supported write file types. Called when master representation is changed
  void ResetSupportedWriteFileTypes();

  /// Controls if segmentation labelmap representation is written using minimum necessary extent
  /// or the extent of reference image.
  /// If false (default): the segmentation will be saved using the same extent as the reference image.
  /// If true: the segmentation will be saved using the effective extent. This saves space (especially when
  /// saving the segmentation uncompressed), but makes voxel coordinates of the segmentation different from
  /// voxel coordinates of the reference image, which may cause problems in software that assume voxel coordinate system
  /// of the reference image is the same as the segmentation's.
  vtkSetMacro(CropToMinimumExtent, bool);
  vtkGetMacro(CropToMinimumExtent, bool);
  vtkBooleanMacro(CropToMinimumExtent, bool);

protected:
  /// Initialize all the supported read file types
  void InitializeSupportedReadFileTypes() override;

  /// Initialize all the supported write file types
  void InitializeSupportedWriteFileTypes() override;

  /// Get data node that is associated with this storage node
  vtkMRMLSegmentationNode* GetAssociatedDataNode();

  /// Write data from a referenced node
  int WriteDataInternal(vtkMRMLNode *refNode) override;

  /// Write binary labelmap representation to file
  virtual int WriteBinaryLabelmapRepresentation(vtkMRMLSegmentationNode* segmentationNode, std::string path);

  /// Write a poly data representation to file
  virtual int WritePolyDataRepresentation(vtkMRMLSegmentationNode* segmentationNode, std::string path);

  /// Read data and set it in the referenced node
  int ReadDataInternal(vtkMRMLNode *refNode) override;

  /// Read binary labelmap representation from nrrd file (3D spatial + list)
  virtual int ReadBinaryLabelmapRepresentation(vtkMRMLSegmentationNode* segmentationNode, std::string path);

#ifdef SUPPORT_4D_SPATIAL_NRRD
  /// Read binary labelmap representation from 4D spatial nrrd file - obsolete
  virtual int ReadBinaryLabelmapRepresentation4DSpatial(vtkMRMLSegmentationNode* segmentationNode, std::string path);
#endif

  /// Read a poly data representation to file
  virtual int ReadPolyDataRepresentation(vtkMRMLSegmentationNode* segmentationNode, std::string path);

  /// Add all files corresponding to poly data representation to the storage node
  /// (multiblock dataset writes segments to individual files in a separate folder)
  void AddPolyDataFileNames(std::string path, vtkSegmentation* segmentation);

  /// Serialize contained representation names in a string
  std::string SerializeContainedRepresentationNames(vtkSegmentation* segmentation);

  /// Create representations based on serialized representation names string
  void CreateRepresentationsBySerializedNames(vtkSegmentation* segmentation, std::string representationNames);

  /// Get the metadata string for the segment and key from the dictionary
  static bool GetSegmentMetaDataFromDicitionary(std::string& headerValue, itk::MetaDataDictionary dictionary, int segmentIndex, std::string keyName);

  /// Get the metadata string for the segmentation key from the dictionary
  static bool GetSegmentationMetaDataFromDicitionary(std::string& headerValue, itk::MetaDataDictionary dictionary, std::string keyName);

  static std::string GetSegmentMetaDataKey(int segmentIndex, const std::string& keyName);

  static std::string GetSegmentationMetaDataKey(const std::string& keyName);

  static std::string GetSegmentTagsAsString(vtkSegment* segment);
  static void SetSegmentTagsFromString(vtkSegment* segment, std::string tagsValue);

  static std::string GetImageExtentAsString(vtkOrientedImageData* image);
  static std::string GetImageExtentAsString(int extent[6]);
  static void GetImageExtentFromString(int extent[6], std::string extentValue);

  static std::string GetSegmentColorAsString(vtkMRMLSegmentationNode* segmentationNode, const std::string& segmentId);
  static void GetSegmentColorFromString(double color[3], std::string colorString);

protected:
  bool CropToMinimumExtent{false};

protected:
  vtkMRMLSegmentationStorageNode();
  ~vtkMRMLSegmentationStorageNode() override;

private:
  vtkMRMLSegmentationStorageNode(const vtkMRMLSegmentationStorageNode&) = delete;
  void operator=(const vtkMRMLSegmentationStorageNode&) = delete;
};

#endif
